//*****************************************************************************
// JFab (http://code.google.com/p/jfab)
// Copyright (c) 2011-2012 JFab.org
// Admin jfab@jeffreythompson.net
//
// See the file "LICENSE.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//*****************************************************************************

package org.jfab.swingui;

import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreeSelectionModel;

import org.apache.commons.lang3.Validate;
import org.jfab.core.model.CGSpec;
import org.jfab.swingui.decorator.CGSpecDecorator;
import org.jfab.swingui.decorator.Decorator;

/**
 * Provides a tree pane for JFab.
 */
public class TreePanel extends JPanel
{
    /** Serial version UID. */
    private static final long serialVersionUID = 1L;

    /** Icon manager. */
    private final IconManager _iconManager;

    /** Scroll pane. */
    private JScrollPane _scrollPane;

    /** Tree selection listeners. */
    private List<TreeSelectionListener> _treeSelectionListeners = new ArrayList<TreeSelectionListener>();

    /** Tree widget. */
    private JTree _treeUI;

    /** Add button panel. */
    private final JPanel _addButtonPanel;

    /** Expand button panel. */
    private final JPanel _expandButtonPanel;

    /** Action manager. */
    private final ActionManager _actionManager;

    /**
     * Construct this object with the given parameters.
     * 
     * @param cgSpec Code generator specification. (required)
     * @param actionManager Action manager. (required)
     * @param iconManager Icon manager. (required)
     */
    public TreePanel(final CGSpec cgSpec, final ActionManager actionManager,
            final IconManager iconManager)
    {
        Validate.notNull(iconManager, "cgSpec is null");
        Validate.notNull(actionManager, "actionManager is null");
        Validate.notNull(iconManager, "iconManager is null");

        setLayout(new BorderLayout());

        _actionManager = actionManager;
        _iconManager = iconManager;
        _expandButtonPanel = createExpandButtonPanel();
        _addButtonPanel = createAddButtonPanel();
        add(_expandButtonPanel, BorderLayout.NORTH);
        add(_addButtonPanel, BorderLayout.SOUTH);
        setCgSpec(cgSpec);
    }

    /**
     * @param listener Tree selection listener.
     */
    public void addTreeSelectionListener(final TreeSelectionListener listener)
    {
        _treeUI.addTreeSelectionListener(listener);
        _treeSelectionListeners.add(listener);
    }

    /**
     * Collapse all tree nodes.
     */
    public void collapseAll()
    {
        for (int i = _treeUI.getRowCount() - 1; i > 0; i--)
        {
            _treeUI.collapseRow(i);
        }
    }

    /**
     * Expand all tree nodes.
     */
    public void expandAll()
    {
        for (int i = 0; i < _treeUI.getRowCount(); i++)
        {
            _treeUI.expandRow(i);
        }
    }

    /**
     * @return the root node.
     */
    public CGSpecDecorator getRootNode()
    {
        return (CGSpecDecorator)_treeUI.getModel().getRoot();
    }

    /**
     * @return the selected object, if any.
     */
    public Decorator<?> getSelectedObject()
    {
        return (Decorator<?>)_treeUI.getLastSelectedPathComponent();
    }

    /**
     * Refresh.
     */
    public void refresh()
    {
        setCgSpec(getCgSpec());
    }

    /**
     * Remove the selected node from the tree.
     * 
     * @return true if the list contained this.
     */
    public boolean removeSelectedNode()
    {
        final Decorator<?> decorator = getSelectedObject();

        return getMyTreeModel().removeNode(decorator);
    }

    /**
     * @param listener Tree selection listener.
     */
    public void removeTreeSelectionListener(final TreeSelectionListener listener)
    {
        _treeUI.removeTreeSelectionListener(listener);
        _treeSelectionListeners.remove(listener);
    }

    /**
     * @param cgSpec cgSpec to set
     */
    public void setCgSpec(final CGSpec cgSpec)
    {
        if (_scrollPane != null)
        {
            removeTreeSelectionListeners(_treeUI);
            remove(_scrollPane);
        }

        _treeUI = createTreeUI(cgSpec);
        addTreeSelectionListeners(_treeUI);
        _scrollPane = new JScrollPane(_treeUI);

        add(_scrollPane, BorderLayout.CENTER);

        revalidate();
        repaint();
    }

    /**
     * @param treeUI Tree widget.
     */
    private void addTreeSelectionListeners(final JTree treeUI)
    {
        for (final TreeSelectionListener listener : _treeSelectionListeners)
        {
            treeUI.addTreeSelectionListener(listener);
        }
    }

    /**
     * @return a new add button panel.
     */
    private JPanel createAddButtonPanel()
    {
        final JButton addButton = new JButton(_actionManager.getAddNodeAction());
        addButton.setText(null);
        final JButton removeButton = new JButton(
                _actionManager.getRemoveNodeAction());
        removeButton.setText(null);

        final JPanel panel = new JPanel();

        panel.add(addButton);
        panel.add(removeButton);

        final JPanel answer = new JPanel();

        answer.add(panel);

        return answer;
    }

    /**
     * @return a new expand button panel.
     */
    private JPanel createExpandButtonPanel()
    {
        final JButton expandButton = new JButton(
                _actionManager.getExpandAllAction());
        expandButton.setText(null);
        final JButton collapseButton = new JButton(
                _actionManager.getCollapseAllAction());
        collapseButton.setText(null);

        final JPanel panel = new JPanel();

        panel.add(expandButton);
        panel.add(collapseButton);

        final JPanel answer = new JPanel();

        answer.add(panel);

        return answer;
    }

    /**
     * @param cgSpec Code generator specification.
     * 
     * @return a new tree widget.
     */
    private JTree createTreeUI(final CGSpec cgSpec)
    {
        final CGSpecTreeModel treeModel = new CGSpecTreeModel(cgSpec,
                _iconManager);
        final JTree answer = new JTree(treeModel);

        answer.getSelectionModel().setSelectionMode(
                TreeSelectionModel.SINGLE_TREE_SELECTION);
        answer.setCellRenderer(new JFabTreeCellRenderer());

        return answer;
    }

    /**
     * @return the code generator specification.
     */
    private CGSpec getCgSpec()
    {
        return ((CGSpecDecorator)getMyTreeModel().getRoot()).getComponent();
    }

    /**
     * @return the tree model.
     */
    private CGSpecTreeModel getMyTreeModel()
    {
        return (CGSpecTreeModel)_treeUI.getModel();
    }

    /**
     * @param treeUI Tree widget.
     */
    private void removeTreeSelectionListeners(final JTree treeUI)
    {
        for (final TreeSelectionListener listener : _treeSelectionListeners)
        {
            treeUI.removeTreeSelectionListener(listener);
        }
    }
}
